#!/usr/bin/env python3
"""
资源监控服务
负责定期监控系统资源使用情况并发送告警
"""

from flask import Flask, jsonify
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from datetime import datetime, timedelta
import logging
from logging.handlers import RotatingFileHandler
import os
import sys
import time
import platform
import psutil
import requests
import socket
import pytz

# 设置默认时区
TIMEZONE = pytz.timezone('Asia/Shanghai')

# 设置时区（仅在Unix/Linux系统上可用）
if platform.system() != 'Windows':
    time.tzset()

# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from system_scripts.resource_monitor import ResourceMonitor
from system_scripts.alert_manager import AlertManager

# 配置日志
if not os.path.exists('logs'):
    os.mkdir('logs')
# 检查是否通过start_services.py启动（会有标准输出重定向到日志文件）
is_started_by_service_manager = sys.stdout.name != '<stdout>'
if not is_started_by_service_manager:
    log_date = datetime.now().strftime('%Y-%m-%d')
    file_handler = RotatingFileHandler(f'logs/monitor_service_{log_date}.log', maxBytes=10240, backupCount=10)
    file_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
    ))
    file_handler.setLevel(logging.INFO)
else:
    # 如果是通过服务管理器启动，则不创建额外的日志文件
    file_handler = logging.StreamHandler()
    file_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
    ))
    file_handler.setLevel(logging.INFO)

class MonitorService:
    """
    资源监控服务
    负责定期监控系统资源使用情况并发送告警
    """

    def __init__(self):
        # 配置日志
        self.logger = logging.getLogger('MonitorService')
        self.logger.setLevel(logging.INFO)
        self.logger.addHandler(file_handler)
        
        # 初始化Flask应用
        self.app = Flask(__name__)
        self.app.logger.addHandler(file_handler)
        
        # 初始化资源监控器和告警管理器
        self.resource_monitor = ResourceMonitor()
        self.alert_manager = AlertManager()
        
        # 初始化调度器
        self.scheduler = BackgroundScheduler(timezone=TIMEZONE)
        
        # 注册路由
        self.register_routes()

    def check_resources(self):
        """检查系统资源使用情况并处理告警"""
        try:
            # 获取资源使用情况
            self.logger.debug("开始检查系统资源使用情况")
            result = self.resource_monitor.check_resource_usage()
            
            # 如果状态不正常，发送告警
            if result['status'] != 'normal':
                self.logger.warning(f"检测到资源异常: {result['status']}，CPU: {result.get('cpu', 'N/A')}%, 内存: {result.get('memory', 'N/A')}%")
                self.alert_manager.process_alert(result)
            else:
                self.logger.debug(f"系统资源正常，CPU: {result.get('cpu', 'N/A')}%, 内存: {result.get('memory', 'N/A')}%")
                
            return result
        except Exception as e:
            self.logger.error(f"资源检查失败: {str(e)}", exc_info=True)
            return None

    # API路由
    def get_monitor_status(self):
        """获取当前资源监控状态"""
        try:
            self.logger.info("API请求: 获取当前资源监控状态")
            result = self.check_resources()
            if result:
                self.logger.info(f"成功获取资源监控状态: {result['status']}")
                return jsonify({
                    'success': True,
                    'data': result
                })
            self.logger.error("获取资源监控状态失败: 结果为空")
            return jsonify({
                'success': False,
                'message': '获取资源监控状态失败'
            }), 500
        except Exception as e:
            self.logger.error(f"获取资源监控状态时发生异常: {str(e)}", exc_info=True)
            return jsonify({
                'success': False,
                'message': str(e)
            }), 500


    def get_alert_history(self):
        """获取告警历史记录"""
        try:
            self.logger.info("API请求: 获取告警历史记录")
            history = self.alert_manager.get_alert_history()
            self.logger.info(f"成功获取告警历史记录，共 {len(history)} 条")
            return jsonify({
                'success': True,
                'data': history
            })
        except Exception as e:
            self.logger.error(f"获取告警历史记录时发生异常: {str(e)}", exc_info=True)
            return jsonify({
                'success': False,
                'message': str(e)
            }), 500


    def get_system_metrics(self):
        """获取系统资源使用指标"""
        try:
            self.logger.info("API请求: 获取系统资源使用指标")
            metrics = self.resource_monitor.get_system_metrics()
            if metrics:
                self.logger.info("成功获取系统资源使用指标")
                return jsonify({
                    'success': True,
                    'data': metrics
                })
            self.logger.error("获取系统指标失败: 结果为空")
            return jsonify({
                'success': False,
                'message': '获取系统指标失败'
            }), 500
        except Exception as e:
            self.logger.error(f"获取系统资源使用指标时发生异常: {str(e)}", exc_info=True)
            return jsonify({
                'success': False,
                'message': str(e)
            }), 500


    def get_process_metrics(self, pid):
        """获取指定进程的资源使用指标"""
        try:
            self.logger.info(f"API请求: 获取进程 {pid} 的资源使用指标")
            metrics = self.resource_monitor.get_process_metrics(pid)
            if metrics:
                self.logger.info(f"成功获取进程 {pid} 的资源使用指标")
                return jsonify({
                    'success': True,
                    'data': metrics
                })
            self.logger.error(f"获取进程 {pid} 指标失败: 结果为空")
            return jsonify({
                'success': False,
                'message': '获取进程指标失败'
            }), 500
        except Exception as e:
            self.logger.error(f"获取进程 {pid} 资源使用指标时发生异常: {str(e)}", exc_info=True)
            return jsonify({
                'success': False,
                'message': str(e)
            }), 500

    def job_listener(self, event):
        """任务执行监听器"""
        if event.exception:
            self.logger.error(f'资源监控任务执行失败: {event.job_id}, 错误: {event.exception}', exc_info=True)
        else:
            self.logger.info(f'资源监控任务执行成功: {event.job_id}')

    def init_scheduler(self):
        """初始化调度器"""
        try:
            self.logger.info("正在初始化资源监控调度器...")
            # 添加资源检查任务，每30秒执行一次
            self.scheduler.add_job(
                func=self.check_resources,
                trigger='interval',
                seconds=30,
                id='resource_check',
                replace_existing=True,
                max_instances=1  # 确保同一时间只有一个任务实例在运行
            )
            self.logger.info("已添加资源检查任务，执行间隔: 30秒")
            
            # 添加任务监听器
            self.scheduler.add_listener(self.job_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
            self.logger.info("已添加任务监听器")
            
            # 启动调度器
            self.scheduler.start()
            self.logger.info("资源监控调度器启动成功")
        except Exception as e:
            self.logger.error(f"资源监控调度器启动失败: {str(e)}", exc_info=True)

    def start(self):
        """启动监控服务"""
        try:
            # 先启动Flask应用
            self.logger.info("正在启动监控服务...")
            self.logger.info(f"日志文件: logs/monitor_service_{datetime.now().strftime('%Y-%m-%d')}.log")
            
            # 在新线程中启动调度器
            import threading
            scheduler_thread = threading.Thread(target=self.init_scheduler)
            scheduler_thread.daemon = True
            scheduler_thread.start()
            self.logger.info("调度器线程已启动")
            
            # 启动Flask应用（主线程）
            self.logger.info("正在启动Flask应用，监听 0.0.0.0:5003")
            self.app.run(host='0.0.0.0', port=5003)
        except Exception as e:
            self.logger.error(f"监控服务启动失败: {str(e)}", exc_info=True)
        finally:
            # 确保在服务停止时关闭调度器
            try:
                if hasattr(self, 'scheduler') and self.scheduler.running:
                    self.scheduler.shutdown(wait=False)
                    self.logger.info("调度器已关闭")
            except Exception as e:
                self.logger.error(f"关闭调度器时发生错误: {str(e)}", exc_info=True)
            self.logger.info("监控服务已停止")

    def get_services_status(self):
        """获取各个服务的在线状态和在线时长"""
        try:
            self.logger.info("API请求: 获取服务在线状态")
            services = [
                {
                    "name": "Web服务",
                    "url": "http://localhost:8088",
                    "status": False,
                    "uptime": None
                },
                {
                    "name": "调度器服务",
                    "url": "http://localhost:5001",
                    "status": False,
                    "uptime": None
                },
                {
                    "name": "执行器服务",
                    "url": "http://localhost:5002",
                    "status": False,
                    "uptime": None
                },
                {
                    "name": "监控服务",
                    "url": "http://localhost:5003",
                    "status": False,
                    "uptime": None
                }
            ]
            
            # 检查各个服务的状态
            for service in services:
                try:
                    # 尝试连接服务
                    response = requests.get(f"{service['url']}/api/health", timeout=2)
                    service["status"] = response.status_code == 200
                except:
                    # 如果连接失败，尝试检查端口是否开放
                    port = int(service["url"].split(":")[-1])
                    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    sock.settimeout(1)
                    result = sock.connect_ex(("localhost", port))
                    sock.close()
                    service["status"] = result == 0
                
                # 如果服务在线，获取进程启动时间
                if service["status"]:
                    port = int(service["url"].split(":")[-1])
                    for proc in psutil.process_iter(['pid', 'name', 'create_time']):
                        try:
                            # 单独获取连接信息
                            try:
                                connections = proc.connections()
                                for conn in connections:
                                    # 确保 conn.laddr 存在且有 port 属性
                                    if hasattr(conn, 'laddr') and hasattr(conn.laddr, 'port') and conn.laddr.port == port:
                                        # 计算运行时长
                                        start_time = datetime.fromtimestamp(proc.info['create_time'])
                                        uptime = datetime.now() - start_time
                                        # 格式化为天、小时、分钟
                                        days = uptime.days
                                        hours, remainder = divmod(uptime.seconds, 3600)
                                        minutes, _ = divmod(remainder, 60)
                                        service["uptime"] = f"{days}天 {hours}小时 {minutes}分钟"
                                        break
                            except (psutil.AccessDenied, AttributeError, psutil.Error) as conn_err:
                                # 无法获取连接信息，跳过此进程
                                continue
                        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                            pass
            
            self.logger.info("成功获取服务在线状态")
            return jsonify(services)
        except Exception as e:
            self.logger.error(f"获取服务在线状态时发生异常: {str(e)}", exc_info=True)
            return jsonify({
                'success': False,
                'message': str(e)
            }), 500
    
    def register_routes(self):
        """注册所有路由"""
        self.app.add_url_rule('/api/monitor/status', 'get_monitor_status', self.get_monitor_status)
        self.app.add_url_rule('/api/monitor/history', 'get_alert_history', self.get_alert_history)
        self.app.add_url_rule('/api/monitor/metrics/system', 'get_system_metrics', self.get_system_metrics)
        self.app.add_url_rule('/api/monitor/metrics/process/<int:pid>', 'get_process_metrics', self.get_process_metrics)
        self.app.add_url_rule('/api/monitor/services', 'get_services_status', self.get_services_status)
        
        # 添加状态路由
        @self.app.route('/status')
        def get_status():
            """获取监控服务状态"""
            return jsonify({
                'status': 'running',
                'timestamp': datetime.now().isoformat(),
                'service': 'monitor'
            })
        
        # 添加健康检查端点
        @self.app.route('/api/health')
        def health_check():
            return jsonify({"status": "ok"}), 200

if __name__ == '__main__':
    service = MonitorService()
    service.start()